#include "unixhead.h"

#include <string>
#include <iostream>
#include <functional>
#include <nlohmann/json.hpp>

#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>
#include <workflow/RedisMessage.h>

using namespace std;

class MuploadServer
{
    using Json = nlohmann::json;
public:
    MuploadServer(unsigned short port)
    : _port(port)
    , _curServerTask(nullptr)
    //, _server(std::bind(&MuploadServer::process, this, std::placeholders::_1))
    , _server([this](WFHttpTask * serverTask){
        this->process(serverTask);
    })
    , _waitGroup(1)
    {}

    void start() {
        if(_server.start(_port) == 0) {
            _waitGroup.wait();
            _server.stop();
        } else {
            cout << "Htttp server cannot start" << endl;
        }
    }

    void process(WFHttpTask * serverTask);

private:
    void init(const string & username);
    void uppart(const string & uploadID, const string & idx);
    void complete(const string & uploadID);

private:
    unsigned short _port;
    WFHttpTask * _curServerTask;
    WFHttpServer _server;
    WFFacilities::WaitGroup _waitGroup;
};


void MuploadServer::process(WFHttpTask * serverTask)
{
    cout << "process is running" << endl;
    _curServerTask = serverTask;
    //解析请求
    auto req = _curServerTask->get_req();
    string method = req->get_method();
    string uri = req->get_request_uri();
    string path = uri.substr(0, uri.find("?"));
    string queryKV = uri.substr(uri.find("?") + 1);
    cout << "method:" << method << endl;
    cout << "path:" << path << endl;
    cout << "queryKV:" << queryKV << endl;

    if(method == "POST" && "/file/mupload/init" == path) {
        // username=lwh
        string username = queryKV.substr(queryKV.find("=") + 1);
        init(username);
    } else if("POST" == method  && "/file/mupload/uppart" == path) {
        // uploadID=lwh2023:06:19-10:31:24&chunkidx=0
        // 解析查询词
        string uploadIDKV = queryKV.substr(0, queryKV.find("&"));
        string chunkidxKV = queryKV.substr(queryKV.find("&") + 1);

        string uploadID = uploadIDKV.substr(uploadIDKV.find("=") + 1);
        string chunkidx = chunkidxKV.substr(chunkidxKV.find("=") + 1);
        cout << "uploadID:" << uploadID << endl
             << "chunkidx:" << chunkidx << endl;
        uppart(uploadID, chunkidx);
    } else if("GET" == method && "/file/mupload/complete" == path) {

        //uploadID=lwh2023:06:19-10:31:24
        string uploadID = queryKV.substr(queryKV.find("=") + 1);
        cout << "uploadID:" << uploadID << endl;
        complete(uploadID);
    }
}

void MuploadServer::init(const string & username)
{
    cout << "init..." << endl;
    //生成uploadID
    time_t t = time(nullptr);
    struct tm * ptm = localtime(&t);
    char buff[100] = {0};
    sprintf(buff, "%s%04d:%02d:%02d-%02d:%02d:%02d",
        username.c_str(), ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday,
        ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
    string uploadID(buff);
    cout << "uploadID:" << uploadID << endl;

    //获取请求消息的消息体
    auto req = _curServerTask->get_req();
    const void * body;
    size_t size = 0;
    req->get_parsed_body(&body, &size);
    //将消息体转换为JSON对象
    Json fileInfo = Json::parse((const char *)(body));

    size_t filesize = fileInfo["filesize"];
    string filename = fileInfo["filename"];
    string filehash = fileInfo["filehash"];


    //生成分片信息
    int chunksize = 1024 * 1024;
    int chunkcount = filesize / chunksize + (filesize % chunksize ? 1 : 0);
    cout << "chunkcount: " << chunkcount << endl;

    string redisurl = "redis://localhost:6379";
    auto redisTask = WFTaskFactory::create_redis_task(redisurl, 0, nullptr);
    redisTask->get_req()->set_request("HSET", {
        uploadID,
        "username", username,
        "filename", filename,
        "filehash", filehash,
        "filesize", to_string(filesize),
        "chunksize", to_string(chunksize),
        "chunkcount", to_string(chunkcount)
    });
    series_of(_curServerTask)->push_back(redisTask);
    
    //生成响应信息
    fileInfo["uploadID"] = uploadID;
    fileInfo["chunksize"] = chunksize;
    fileInfo["chunkcount"] = chunkcount;

    auto resp = _curServerTask->get_resp();
    string msg = fileInfo.dump();
    resp->append_output_body(msg);
    resp->add_header_pair("Content-Type", "application/json");
    resp->add_header_pair("Content-Length", to_string(msg.size()));
    resp->add_header_pair("Server", "Workflow-Server");
}

void MuploadServer::uppart(const string & uploadID, const string & idx)
{
    cout << "uppart..." << endl;
    //获取分片内容
    auto req = _curServerTask->get_req();
    const void * body;
    size_t size = 0;
    req->get_parsed_body(&body, &size);
    //用uploadID创建文件夹
    mkdir(uploadID.c_str(), 0755);
    string chunkfile = uploadID + "/" + idx;
    int fd = open(chunkfile.c_str(), O_CREAT|O_RDWR, 0664);
    if(fd < 0) {
        perror("open");
        return;
    }

    //写入本地文件
    int ret = write(fd, body, size);
    cout << "write into file " << ret << " bytes" << endl;
    close(fd);

    //将分片信息写入到Redis中
    string redisurl = "redis://localhost:6379";
    auto redisTask = WFTaskFactory::create_redis_task(redisurl, 0, nullptr);
    redisTask->get_req()->set_request("HSET", {
        uploadID,
        "chunkidx_" + idx,
        "1"
    });
    series_of(_curServerTask)->push_back(redisTask);
    
    //生成响应信息
    auto resp = _curServerTask->get_resp();
    string msg("upload uppart success");
    resp->append_output_body(msg);
    resp->add_header_pair("Content-Type", "text/plain");
    resp->add_header_pair("Content-Length", to_string(msg.size()));
} 

void MuploadServer::complete(const string & uploadID)
{
    cout << "complete..." << endl;
    string redisurl = "redis://localhost:6379";
    auto redisTask = WFTaskFactory::create_redis_task(redisurl, 0, 
    [this](WFRedisTask * redistask){
        auto resp = redistask->get_resp();
        int state = redistask->get_state();
        int error = redistask->get_error();
        if(state != WFT_STATE_SUCCESS) {
            printf("error: %s\n",WFGlobal::get_error_string(state, error));
            return;
        }

        protocol::RedisValue result;
        resp->get_result(result);
        int chunkcount = -1;
        int current = 0;
        auto httpResp = this->_curServerTask->get_resp();
        string msgfail = "upload failed";
        string msgsuccess = "upload success";
        httpResp->add_header_pair("Content-Type", "text/plain");
        if(result.is_array()) {
            for(size_t i = 0; i < result.arr_size(); i +=2 ) {
                string key = result.arr_at(i).string_value();
                string val = result.arr_at(i + 1).string_value();
                if(key == "chunkcount") {
                    chunkcount = stoi(val);
                }

                if(key.substr(0, 9) == "chunkidx_") {
                    ++current;
                }
            }
            cout << "chunkcount:" << chunkcount << endl;
            cout << "current:" << current << endl;
            if(chunkcount == -1 || chunkcount != current) {
                //没有上传成功
                httpResp->append_output_body(msgfail);
                httpResp->add_header_pair("Content-Length", to_string(msgfail.size()));
            }else if(chunkcount == current){
                //所有的分片都上传了
                //合并文件...
                httpResp->append_output_body(msgsuccess);
                httpResp->add_header_pair("Content-Length", to_string(msgsuccess.size()));
            }
        } else {
            //没有上传成功
            httpResp->append_output_body(msgfail);
            httpResp->add_header_pair("Content-Length", to_string(msgfail.size()));
        }
    });
    redisTask->get_req()->set_request("HGETALL", {uploadID});
    series_of(_curServerTask)->push_back(redisTask);

}


void test0()
{
    MuploadServer server(8888);
    server.start();
}


int main()
{
    test0();
    return 0;
}

